iT邦幫忙

2021 iThome 鐵人賽

DAY 6
0
永豐金融APIs

永豐 API 隨意玩系列 第 6

Day06 - 隨意玩之 OrderQuery/OrderPayQuery

  • 分享至 

  • xImage
  •  

昨天介紹了 OrderCreate,今天會把另外兩個都介紹完!


OrderQuery

  • 主要功能:查詢訂單
  • 查詢方式有幾種
    • OrderNo: 根據訂單編號 (必要參數)
    • PayType: 根據收款方式
    • OrderDateTimeS: 交易日期(起)
    • OrderDateTimeE: 交易日期(迄)
    • PayDateTimeS: 付款日期(起)
    • PayDateTimeE: 付款日期(迄)
    • PayFlag: 付款狀態
  • 查詢時必須要有 訂單編號收款方式交易起迄或是付款狀態,其中一種!
  • 每次查詢的上限為 300 筆,所以範圍不要設太大~

例如我們想要查詢:信用卡付款 + 付款完成的訂單就可以這樣下

{
    "ShopNo": shop_no, 
    "PayType": "C",
    "PayFlag": "Y"
}

收到的回應,會像是下面這樣~
訊息的意思可以參考 Spec,內容有點多

{
   "ShopNo":"NA0249_001",
   "Date":"202109162125",
   "Status":"S",
   "Description":"S0000 – 處理成功",
   "OrderList":[
      {
         "OrderNo":"A202109150006",
         "TSNo":"NA024900000158",
         "TSDate":"202109151927",
         "ApprovedDate":"202109151927",
         "PayDate":"202109151927",
         "Amount":51000,
         "PayType":"C",
         "PayStatus":"1C400",
         "ExpireDate":"202109151938",
         "RefundFlag":"N",
         "RefundStatus":"",
         "RefundDate":"",
         "PrdtName":"OrderCreate_CreditCard_Example",
         "CardParam":{
            "CardPayURL":"https://sandbox.sinopac.com/QPay.WebPaySite/Bridge/PayCard?TD=NA024900000158&TK=38e938a1-bb5f-49b5-b173-b402088bb279",
            "LeftCCNo":"499937",
            "RightCCNo":"3145",
            "AuthCode":"F56172",
            "CCExpDate":"",
            "CCToken":""
         },
         "RefundAmount":0
      }
   ]
}

OrderQuery 大概就是這樣,讓我們繼續下一個

OrderPayQuery

  • 主要功能:查詢訂單內容
  • 需要的參數只有
    • ShopNO
    • PayToken: 訊息 Token

PayToken 會在使用者付款時,傳送給 ReturnURL

於是我寫了一個小小的程式去接收訊息,並把拿到的 Token 顯示出來
https://ithelp.ithome.com.tw/upload/images/20210916/20141787eHkToYT0ur.png

回傳訊息如下,可以看到訂單是否處理或是付款有沒有成功~
訊息的意思一樣參考 Spec,內容有點多

{
   "ShopNo":"NA0249_001",
   "PayToken":"1fda3bb101e044840486e8f3489ecea923ac51aacf55fef27a080170c193e025",
   "Date":"202109162140",
   "Status":"S",
   "Description":"S0000 – 處理成功",
   "TSResultContent":{
      "APType":"PayOut",
      "TSNo":"NA024900000167",
      "OrderNo":"A202109160001",
      "ShopNo":"NA0249_001",
      "PayType":"C",
      "Amount":"51000",
      "Status":"S",
      "Description":"",
      "LeftCCNo":"",
      "RightCCNo":"",
      "CCExpDate":"",
      "CCToken":"",
      "PayDate":"202109162137"
   }
}

今天大概就說這樣,然後明天要講什麼還不確定!

from __future__ import unicode_literals
import requests
import hashlib
import codecs
import json
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad

shop_no = 'NA0249_001'
sinopac_hash = {
	'a1': '',
	'a2': '',
	'b1': '',
	'b2': ''
}

nonce_url = 'https://apisbx.sinopac.com/funBIZ/QPay.WebAPI/api/Nonce'
order_url = 'https://apisbx.sinopac.com/funBIZ/QPay.WebAPI/api/Order'

def getNonce():
	nonce_data = {
		'ShopNo': shop_no
	}

	r = requests.post(nonce_url, json=nonce_data)

	return json.loads(r.content)['Nonce']

def calcHashID():
	a1 = sinopac_hash['a1']
	a2 = sinopac_hash['a2']
	b1 = sinopac_hash['b1']
	b2 = sinopac_hash['b2']

	xor1 = hex(int(a1, base=16)^int(a2, base=16))
	xor2 = hex(int(b1, base=16)^int(b2, base=16))

	hash_id = xor1[2:]+xor2[2:]
	return hash_id.upper()

def calcIV(nonce):
	s = hashlib.sha256()

	s.update(nonce.encode('utf-8'))
	h = s.hexdigest()
	return h[-16:].upper()

def calcSign(msg_content, nonce, hash_id):
	sign_msg = msg_content+nonce+hash_id

	s = hashlib.sha256()
	s.update(sign_msg.encode('utf-8'))
	h = s.hexdigest()
	return h.upper()

def parseQueryData(msg_param):
    if type(msg_param) != dict:
        return
    
    order_message = dict(sorted(msg_param.items(), key = lambda x: x[0]))
    message = ''

    for k, v in order_message.items():
        if type(v) == dict or v == '':
            continue
        message += f"{k}={v}&"

    return message[:-1]

def CBCEncrypt(key, iv, data):
    cipher = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv.encode('utf-8'))
    return (cipher.encrypt(pad(data.encode('utf-8'), AES.block_size)))

def CBCDecrypt(key, iv, data):
    data = codecs.decode(data, "hex")
    cipher = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv.encode('utf-8'))
    return unpad(cipher.decrypt(data), AES.block_size)

def apiService(service, sign, nonce, msg):
	api_data = {
		'Version': '1.0.0',
		'ShopNo': shop_no,
		'APIService': service,
		'Sign': sign,
		'Nonce': nonce,
		'Message': msg
	}

	r = requests.post(order_url, json = api_data)
	resp = json.loads(r.content)

	return (resp['Sign'], resp['Nonce'], resp['Message'])

## 這邊修改成每個 API 都共用
def queryAPI(api, api_msg):
	nonce = getNonce()
	hash_id = calcHashID()
	iv = calcIV(nonce)
	content = parseQueryData(api_msg)
	signature = calcSign(content, nonce, hash_id)
	msg = json.dumps(api_msg, ensure_ascii=False).replace(' ','')
	enc_message = CBCEncrypt(hash_id, iv, msg).hex().upper()
	if api == "C":
		resp_sign, resp_nonce, resp_message = apiService('OrderCreate', signature, nonce, enc_message)
	elif api == "Q":
		resp_sign, resp_nonce, resp_message = apiService('OrderQuery', signature, nonce, enc_message)
	elif api == "PQ":
		resp_sign, resp_nonce, resp_message = apiService('OrderPayQuery', signature, nonce, enc_message)
	else:
		return
	resp_iv = calcIV(resp_nonce)
	dec_msg = CBCDecrypt(hash_id, resp_iv, resp_message)
	
	return json.loads(dec_msg)


order_create = {
    "ShopNo": shop_no,
    "OrderNo": "A202109160003",
    "Amount": 51000,
    "CurrencyID": "TWD",
    "PayType": "C",
    "CardParam": {
        "AutoBilling": "Y"
    },
    "PrdtName": "OrderCreate_CreditCard_Example",
    "ReturnURL": "<your_return_url>",
    "BackendURL": "<your_backend_url>"
}


order_query = {
    "ShopNo": shop_no, 
    "PayType": "C",
    "PayFlag": "Y"
}

order_pay_query = {
	"ShopNo": shop_no, 
    "PayToken": "1fda3bb101e044840486e8f3489ecea923ac51aacf55fef27a080170c193e025"
}

print(queryAPI("C", order_create))
print(queryAPI("Q", order_query))
print(queryAPI("PQ", order_pay_query))

上一篇
Day05 - 隨意玩之 OrderCreate API
系列文
永豐 API 隨意玩6
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言